commonlibsse_ng\re\t/
TESQuest.rs

1use crate::re::BGSScene;
2use crate::re::BGSStoryManagerTreeForm::{BGSStoryManagerTreeForm, BGSStoryManagerTreeFormVtbl};
3use crate::re::BGSStoryTeller::BGSStoryTeller;
4use crate::re::BSAtomic::BSReadWriteLock;
5use crate::re::BSFixedString::BSFixedString;
6use crate::re::BSPointerHandle::ObjectRefHandle;
7use crate::re::BSString::BSString;
8use crate::re::BSTArray::BSTArray;
9use crate::re::BSTHashMap::{BSTHashMap, UnkKey, UnkValue};
10use crate::re::BSTList::BSSimpleList;
11use crate::re::DialogueTypes::{DIALOGUE_TYPE, DIALOGUE_TYPE_CEnum};
12use crate::re::FormTypes::FormType;
13use crate::re::NiSmartPointer::NiPointer;
14use crate::re::QuestEvent::QuestEvent;
15use crate::re::QuestObjectiveStates::QUEST_OBJECTIVE_STATE;
16use crate::re::TESCondition::TESCondition;
17use crate::re::TESForm::{DerivedTESForm, TESForm};
18use crate::re::TESFullName::{TESFullName, TESFullNameVtbl};
19use crate::re::TESGlobal::TESGlobal;
20use crate::re::offsets_rtti::RTTI_TESQuest;
21use crate::re::offsets_vtable::VTABLE_TESQuest;
22use crate::re::{BGSBaseAlias, BGSDialogueBranch, QueuedPromoteQuestTask, TESTopic};
23use crate::rel::id::VariantID;
24
25#[commonlibsse_ng_derive_internal::to_bitflags]
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[repr(u16)]
28pub enum QuestFlag {
29    StopStart = 65535, // cast -1
30    None = 0,
31    Enabled = 1 << 0,
32    Completed = 1 << 1,
33    AddIdleToHello = 1 << 2,
34    AllowRepeatStages = 1 << 3,
35    StartsEnabled = 1 << 4,
36    DisplayedInHUD = 1 << 5,
37    Failed = 1 << 6,
38    StageWait = 1 << 7,
39    RunOnce = 1 << 8,
40    ExcludeFromExport = 1 << 9,
41    WarnOnAliasFillFailure = 1 << 10,
42    Active = 1 << 11,
43    RepeatsConditions = 1 << 12,
44    KeepInstance = 1 << 13,
45    WantDormant = 1 << 14,
46    HasDialogueData = 1 << 15,
47}
48
49#[commonlibsse_ng_derive_internal::to_bitflags]
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
51#[repr(u32)]
52pub enum QUEST_OBJECTIVE_FLAGS {
53    None = 0,
54    ORWithPrevious = 1 << 0,
55    NoStatsTracking = 1 << 1,
56}
57
58#[repr(C)]
59#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60pub struct StringData {
61    pub aliasId: u32,
62    pub fullNameFormID: u32,
63}
64const _: () = assert!(core::mem::size_of::<StringData>() == 0x8);
65
66#[repr(C)]
67#[derive(Debug, Clone, PartialEq)]
68pub struct GlobalValueData {
69    pub global: *const TESGlobal, // 0x000
70    pub value: f32,               // 0x008
71    pub pad0C: u32,               // 0x00C
72}
73const _: () = assert!(core::mem::size_of::<GlobalValueData>() == 0x10);
74
75#[repr(C)]
76#[derive(Debug, Clone)]
77pub struct BGSQuestInstanceText {
78    id: u32,                              // 0x000
79    pad04: u32,                           // 0x004
80    stringData: BSTArray<StringData>,     // 0x008
81    valueData: BSTArray<GlobalValueData>, // 0x020
82    journalStage: u16,                    // 0x038
83    journalStageItem: u8,                 // 0x03A
84    pad3B: u8,                            // 0x03B
85    pad3C: u32,                           // 0x03C
86}
87const _: () = assert!(core::mem::size_of::<BGSQuestInstanceText>() == 0x40);
88
89/// - C++ `Type`
90#[commonlibsse_ng_derive_internal::ffi_enum]
91#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
92#[repr(u8)]
93pub enum QuestType {
94    None = 0,
95    MainQuest = 1,
96    MagesGuild = 2,
97    ThievesGuild = 3,
98    DarkBrotherhood = 4,
99    CompanionsQuest = 5,
100    Miscellaneous = 6,
101    Daedric = 7,
102    SideQuest = 8,
103    CivilWar = 9,
104    DLC01Vampire = 10,
105    DLC02Dragonborn = 11,
106}
107
108#[repr(C)]
109#[derive(Debug, Clone)]
110pub struct QUEST_DATA {
111    pub questDelayTime: f32,  // 0x000
112    pub flags: QuestFlag,     // 0x004
113    pub priority: i8,         // 0x006
114    pub questType: QuestType, // 0x007
115}
116const _: () = assert!(core::mem::size_of::<QUEST_DATA>() == 0x8);
117
118#[commonlibsse_ng_derive_internal::to_bitflags]
119#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
120#[repr(u8)]
121pub enum QuestStageFlag {
122    #[default]
123    None = 0,
124    StartUpStage = 1 << 1,
125    ShutDownStage = 1 << 2,
126    KeepInstanceDataFromHereOn = 1 << 3,
127}
128
129#[repr(C)]
130#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
131pub struct QUEST_STAGE_DATA {
132    pub index: u16,            // 0x000
133    pub flags: QuestStageFlag, // 0x002
134    pad3: u8,                  // 0x003
135    pad4: u32,                 // 0x004
136}
137const _: () = assert!(core::mem::size_of::<QUEST_STAGE_DATA>() == 0x8);
138
139#[repr(C)]
140#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
141pub struct TESQuestStage {
142    pub data: QUEST_STAGE_DATA,
143}
144const _: () = assert!(core::mem::size_of::<TESQuestStage>() == 0x8);
145
146// Safety: All of these fields can be zero initialized.
147unsafe impl std_fork::zeroable::Zeroable for TESQuestStage {}
148
149#[commonlibsse_ng_derive_internal::to_bitflags]
150#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
151#[repr(u8)]
152pub enum TESQuestTargetFlag {
153    None = 0,
154    CompassMarkerIgnoresLocks = 1 << 0,
155}
156
157#[repr(C)]
158#[derive(Debug, PartialEq)]
159pub struct TESQuestTarget {
160    pub unk00: u64,               // 0x00
161    pub conditions: TESCondition, // 0x08
162    pub alias: u8,                // 0x10
163    pub unk11: u8,                // 0x11
164    pub unk12: u16,               // 0x12
165    pub unk14: u32,               // 0x14
166}
167const _: () = assert!(core::mem::size_of::<TESQuestTarget>() == 0x18);
168
169#[repr(C)]
170#[derive(Debug, Clone)]
171pub struct BGSQuestObjective {
172    displayText: BSFixedString,        // 0x00 - NNAM
173    ownerQuest: *mut TESQuest,         // 0x08
174    targets: *mut *mut TESQuestTarget, // 0x10 - QSTA
175    numTargets: u32,                   // 0x18
176    index: u16,                        // 0x1C - QOBJ
177    initialized: bool,                 // 0x1E
178    state: QUEST_OBJECTIVE_STATE,      // 0x1F
179    flags: QUEST_OBJECTIVE_FLAGS,      // 0x20 - FNAM
180    pad24: u32,                        // 0x24
181}
182const _: () = assert!(core::mem::size_of::<BGSQuestObjective>() == 0x28);
183
184#[repr(C)]
185#[derive(Debug, Clone)]
186pub struct BGSStoryEvent {
187    pub id: u32,
188    pub index: u32,
189    pub members: [u64; 6],
190}
191const _: () = assert!(core::mem::size_of::<BGSStoryEvent>() == 0x38);
192
193#[commonlibsse_ng_derive_internal::to_bitflags]
194#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
195#[repr(u32)]
196pub enum ChangeFlag {
197    QuestFlags = 1 << 1,
198    QuestScriptDelay = 1 << 2,
199    QuestAlreadyRun = 1 << 26,
200    QuestInstanceData = 1 << 27,
201    QuestRuntimeData = 1 << 28,
202    QuestObjectives = 1 << 29,
203    QuestScript = 1 << 30,
204    QuestStages = 1 << 31,
205}
206
207#[commonlibsse_ng_derive_internal::to_bitflags]
208#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
209#[repr(u32)]
210pub enum RecordFlag {
211    Deleted = 1 << 5,
212    Ignored = 1 << 12,
213}
214
215const BRANCHED_TOTAL: usize = DIALOGUE_TYPE::BRANCHED_TOTAL;
216const TOPICS_SIZE: usize = DIALOGUE_TYPE_CEnum::count() - BRANCHED_TOTAL;
217
218#[repr(C)]
219#[derive(Debug)]
220pub struct TESQuest {
221    pub __base: BGSStoryManagerTreeForm,                      // 0x000
222    pub __base1: TESFullName,                                 // 0x028
223    pub instanceData: BSTArray<*mut BGSQuestInstanceText>,    // 0x038
224    pub currentInstanceID: u32,                               // 0x050
225    pub pad054: u32,                                          // 0x054
226    pub aliases: BSTArray<*mut BGSBaseAlias>,                 // 0x058
227    pub refAliasMap: BSTHashMap<u32, ObjectRefHandle>,        // 0x070
228    pub unk0A0: BSTHashMap<UnkKey, UnkValue>,                 // 0x0A0 - alias related
229    pub aliasAccessLock: BSReadWriteLock,                     // 0x0D0
230    pub data: QUEST_DATA,                                     // 0x0D8 - DNAM
231    pub eventID: QuestEvent,                                  // 0x0E0 - ENAM
232    pub pad0E4: u32,                                          // 0x0E4
233    pub executedStages: *mut BSSimpleList<TESQuestStage>,     // 0x0E8
234    pub waitingStages: *mut BSSimpleList<*mut TESQuestStage>, // 0x0F0
235    pub objectives: BSSimpleList<*mut BGSQuestObjective>,     // 0x0F8
236    pub objConditions: TESCondition,                          // 0x108
237    pub storyManagerConditions: TESCondition,                 // 0x110
238    pub branchedDialogue:
239        [BSTHashMap<*mut BGSDialogueBranch, *mut BSTArray<*mut TESTopic>>; BRANCHED_TOTAL], // 0x118
240    pub topics: [BSTArray<*mut TESTopic>; TOPICS_SIZE],       // 0x178
241    pub scenes: BSTArray<*mut BGSScene>,                      // 0x208
242    pub textGlobals: *mut BSTArray<*mut TESGlobal>,           // 0x220 - QTGL
243    pub currentStage: u16,                                    // 0x228
244    pub alreadyRun: bool,                                     // 0x22A
245    pub pad22B: u8,                                           // 0x22B
246    pub pad22C: u32,                                          // 0x22C
247    pub formEditorID: BSString,                               // 0x230
248    pub startEventData: *const BGSStoryEvent,                 // 0x240
249    pub promoteTask: NiPointer<QueuedPromoteQuestTask>,       // 0x248
250    pub promotedRefs: BSTArray<ObjectRefHandle>,              // 0x250
251}
252const _: () = assert!(core::mem::size_of::<TESQuest>() == 0x268);
253
254impl TESQuest {
255    pub const RTTI: VariantID = RTTI_TESQuest;
256    pub const VTABLE: [VariantID; 2] = VTABLE_TESQuest;
257
258    /// Get vtable this class.
259    ///
260    /// # Panics
261    /// If vtable is null.
262    #[inline]
263    pub const fn vtable(&self) -> &TESQuestVtbl {
264        let v_ptr = self.__base.__base.__base.vtable;
265        debug_assert!(!v_ptr.is_null(), "BGSStoryTellerVtbl ptr must not be null ptr");
266        unsafe { v_ptr.cast::<TESQuestVtbl>().as_ref().unwrap() }
267    }
268
269    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 24537, ae_id = 25066)]
270    pub fn create_ref_handle_by_alias_id(
271        handle: &ObjectRefHandle,
272        alias_id: u32,
273    ) -> *mut ObjectRefHandle {
274    }
275
276    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 24481, ae_id = 25003)]
277    pub fn ensure_quest_started(result: &mut bool, start_now: bool) -> bool {}
278
279    #[inline]
280    pub const fn get_current_stage_id(&self) -> u16 {
281        self.currentStage
282    }
283
284    #[inline]
285    pub const fn is_active(&self) -> bool {
286        self.data.flags.contains(QuestFlag::Active)
287    }
288
289    #[inline]
290    pub const fn is_completed(&self) -> bool {
291        self.data.flags.contains(QuestFlag::Completed)
292    }
293
294    #[inline]
295    pub const fn is_enabled(&self) -> bool {
296        self.data.flags.contains(QuestFlag::Enabled)
297    }
298
299    #[inline]
300    pub fn is_running(&self) -> bool {
301        !self.is_stopping() && self.promoteTask.is_null()
302    }
303
304    #[inline]
305    pub fn is_starting(&self) -> bool {
306        self.is_enabled()
307            && (self.data.flags == QuestFlag::StopStart || !self.promoteTask.is_null())
308    }
309
310    #[inline]
311    pub const fn is_stopped(&self) -> bool {
312        !(self.data.flags.contains(QuestFlag::Enabled)
313            || self.data.flags.contains(QuestFlag::StageWait))
314    }
315
316    #[inline]
317    pub fn is_stopping(&self) -> bool {
318        !self.is_enabled() && self.data.flags == QuestFlag::StopStart
319    }
320
321    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 24486, ae_id = 25014)]
322    pub fn reset(&mut self) {}
323
324    pub fn reset_and_update(&mut self) {
325        self.reset();
326
327        let enabled = self.is_enabled();
328        if enabled != self.starts_enabled() {
329            if let Some(story_teller) = BGSStoryTeller::get_singleton_mut() {
330                if enabled {
331                    story_teller.begin_start_up_quest(self);
332                } else {
333                    story_teller.begin_shut_down_quest(self);
334                }
335            }
336        }
337    }
338
339    pub fn set_enabled(&mut self, value: bool) {
340        if value {
341            self.data.flags.insert(QuestFlag::Enabled);
342        } else {
343            self.data.flags.remove(QuestFlag::Enabled);
344        }
345
346        let add_change_fn = self.__base.__base.vtable().AddChange;
347        add_change_fn(&mut self.__base.__base, ChangeFlag::QuestFlags.bits());
348    }
349
350    pub fn start(&mut self) -> bool {
351        if self.eventID != QuestEvent::None {
352            #[cfg(feature = "tracing")]
353            tracing::warn!("Attempting to start event scoped quest outside of story manager");
354            return false;
355        }
356
357        let mut result = false;
358        Self::ensure_quest_started(&mut result, true)
359    }
360
361    #[inline]
362    pub const fn starts_enabled(&self) -> bool {
363        self.data.flags.contains(QuestFlag::StartsEnabled)
364    }
365
366    #[inline]
367    pub fn stop(&mut self) {
368        if self.is_enabled() {
369            self.set_enabled(false);
370        }
371    }
372}
373
374impl DerivedTESForm for TESQuest {
375    const FORM_TYPE: FormType = FormType::Quest;
376
377    #[inline]
378    fn get_form(&self) -> &TESForm {
379        &self.__base.__base
380    }
381}
382
383#[repr(C)]
384pub struct TESQuestVtbl {
385    pub __base: BGSStoryManagerTreeFormVtbl, // 0x000
386    pub __base1: TESFullNameVtbl,            //
387}